﻿using System;
using UnityEngine;

namespace FPLibrary
{
    /// <summary>
    /// A simple fixed point rect structure.
    /// </summary>
    [Serializable]
    public struct FPBB
    {
        /// <summary>
        /// The minimum point of the box.
        /// </summary>
        public FPVector Min;


        /// <summary>
        /// The maximum point of the box.
        /// </summary>
        public FPVector Max;


        public static readonly FPBB zero;


        /// <param name="min">The minimum vertex of the bounding box.</param>
        /// <param name="max">The maximum vertex of the bounding box.</param>
        public FPBB(FPVector min, FPVector max)
        {
            this.Min = min;
            this.Max = max;
        }

        static FPBB()
        {
            zero = new FPBB(new FPVector(0, 0, 0), new FPVector(0, 0, 0));
        }

        /// <summary>
        /// Returns the width of the bounding box
        /// </summary>
        public Fix64 Width
        {
            get { return this.Max.x - this.Min.x; }
        }

        /// <summary>
        /// Returns the height of the bounding box
        /// </summary>
        public Fix64 Height
        {
            get { return this.Max.y - this.Min.y; }
        }

        /// <summary>
        /// Returns the height of the bounding box
        /// </summary>
        public Fix64 Depth
        {
            get { return this.Max.z - this.Min.z; }
        }

        /// <summary>
        /// Returns the size of the bounding box
        /// </summary>
        public FPVector Size
        {
            get { return this.Max - this.Min; }
        }

        /// <summary>
        /// Returns the size of the bounding box
        /// </summary>
        public FPVector Center
        {
            get { return (this.Max + this.Min) * 0.5; }
        }

        public bool Intersects(FPBB bb)
        {
            //empty bound box intersects with nothing
            if(this.Max == this.Min || bb.Max == bb.Min)
                return false;

            if (this.Max.x < bb.Min.x || bb.Max.x < this.Min.x)
                return false;
            if (this.Max.y < bb.Min.y || bb.Max.y < this.Min.y)
                return false;
            if (this.Max.z < bb.Min.z || bb.Max.z < this.Min.z)
                return false;
            return true;
        }

        public FPBB Intersection(FPBB bb)
        {
            if(Intersects(bb))
            {
                FPBB result = new FPBB(FPVector.zero, FPVector.zero);

                result.Min.x = FPMath.Max(this.Min.x, bb.Min.x);
                result.Min.y = FPMath.Max(this.Min.y, bb.Min.y);
                result.Min.z = FPMath.Max(this.Min.z, bb.Min.z);

                result.Max.x = FPMath.Min(this.Max.x, bb.Max.x);
                result.Max.y = FPMath.Min(this.Max.y, bb.Max.y);
                result.Max.z = FPMath.Min(this.Max.z, bb.Max.z);

                return result;
            }

            return FPBB.zero;
        }

        public Bounds ToBounds()
        {
            //return new Bounds(this.Min.ToVector(), this.Max.ToVector());
            return new Bounds((this.Min.ToVector() + this.Max.ToVector())*0.5f, this.Max.ToVector() - this.Min.ToVector());
        }

        public FPBB Offseted(FPVector offset)
        {
            return new FPBB(this.Min + offset, this.Max + offset);
        }

        public void Mirror(Fix64 x)
        {
            Fix64 temp = this.Min.x;
            this.Min.x = x + x - this.Max.x;
            this.Max.x = x + x - temp;
        }

        public void Expand(FPBB bb)
        {
            this.Min.x = FPMath.Min(this.Min.x, bb.Min.x);
            this.Min.y = FPMath.Min(this.Min.y, bb.Min.y);
            this.Min.z = FPMath.Min(this.Min.z, bb.Min.z);

            this.Max.x = FPMath.Max(this.Max.x, bb.Max.x);
            this.Max.y = FPMath.Max(this.Max.y, bb.Max.y);
            this.Max.z = FPMath.Max(this.Max.z, bb.Max.z);
        }

        public static FPBB ToFPBB(Bounds bounds)
        {
            FPBB fpbb = new FPBB()
            {
                Min = new FPVector(bounds.min.x, bounds.min.y, bounds.min.z),
                Max = new FPVector(bounds.max.x, bounds.max.y, bounds.max.z)
            };
            return fpbb;
        }
    }
}